/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.wsonrpc.json.support.jackson;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import net.apexes.wsonrpc.core.exception.JsonException;
import net.apexes.wsonrpc.json.JsonRpcError;
import net.apexes.wsonrpc.json.JsonRpcErrorCause;
import net.apexes.wsonrpc.json.JsonRpcResponse;

import java.lang.reflect.Type;

/**
 * @author <a href=mailto:hedyn@foxmail.com>HeDYn</a>
 */
class JacksonJsonRpcResponse implements JsonRpcResponse {

    private final ObjectMapper objectMapper;
    private final ObjectNode objectNode;
    private final String id;
    private final JsonNode result;
    private final JsonRpcError error;

    protected JacksonJsonRpcResponse(ObjectMapper objectMapper, ObjectNode objectNode) {
        this.objectMapper = objectMapper;
        this.objectNode = objectNode;

        if (objectNode.has("id")) {
            JsonNode node = objectNode.get("id");
            if (node.isNull()) {
                this.id = null;
            } else {
                this.id = node.textValue();
            }
        } else {
            this.id = null;
        }
        if (objectNode.has("result")) {
            this.result = objectNode.get("result");
            this.error = null;
        } else {
            JsonNode errorNode = objectNode.get("error");
            int code = errorNode.get("code").intValue();
            String message = null;
            JsonRpcErrorCause data = null;
            if (errorNode.has("message")) {
                JsonNode node = errorNode.get("message");
                if (!node.isNull()) {
                    message = node.textValue();
                }
            }
            if (errorNode.has("data")) {
                JsonNode dataNode = errorNode.get("data");
                if (!dataNode.isNull()) {
                    data = objectMapper.convertValue(dataNode, JsonRpcErrorCause.class);
                }
            }
            this.result = null;
            this.error = new JsonRpcError(code, message, data);
        }
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public Object getResult() {
        return result;
    }

    @Override
    public Object getResultValue(Type type) {
        if (result == null) {
            return null;
        }
        return objectMapper.convertValue(result, objectMapper.constructType(type));
    }

    @Override
    public String getResultAsJson() throws JsonException {
        try {
            return objectMapper.writeValueAsString(result);
        } catch (JsonProcessingException e) {
            throw new JsonException(e);
        }
    }

    @Override
    public JsonRpcError getError() {
        return error;
    }

    @Override
    public String toJson() {
        try {
            return objectMapper.writeValueAsString(objectNode);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean isRequest() {
        return false;
    }

    @Override
    public boolean isResponse() {
        return true;
    }
}
